package utils

import (
	"math"
)

type PointXy struct {
	X, Y float64
}

func (this *PointXy) Same(v *PointXy) bool {
	return this.X == v.X && this.Y == v.Y
}

// 最好切换成复数计算
func crossProduct(x1, y1, x2, y2 float64) float64 {
	// 向量(x1，y1)叉乘向量(x2，y2)
	return x1*y2 - y1*x2
}

/*
使用叉乘方式判断点是否在三角形内(有问题, 总是失败)
*/
func TriangleContainCrossProduct(p1, p2, p3 PointXy, p PointXy) bool {
	// 调整顺序
	if crossProduct(p3.X-p1.X, p3.Y-p1.Y, p2.X-p1.X, p2.Y-p1.Y) >= 0 {
		p2.X, p2.Y, p3.X, p3.Y = p3.X, p3.Y, p2.X, p2.Y
	}
	// switch
	if crossProduct(p2.X-p1.X, p2.Y-p1.Y, p.X-p1.X, p.Y-p1.Y) < 0 ||
		crossProduct(p3.X-p2.X, p3.Y-p2.Y, p.X-p2.X-p.X, p.Y-p2.Y) < 0 ||
		crossProduct(p1.X-p3.X, p1.Y-p3.Y, p.X-p3.X, p.Y-p3.Y) < 0 {
		return false
	}

	return true
}

// 计算两点的距离
func CalcuLength(p1, p2 PointXy) float64 {
	a := math.Abs(p1.X - p2.X)
	b := math.Abs(p1.Y - p2.Y)
	return math.Sqrt(a*a + b*b)
}

// 根据三点坐标计算三角形面积(海伦公式)
func CalcuTriangleArea(p1, p2, p3 PointXy) float64 {
	l1 := CalcuLength(p1, p2)
	l2 := CalcuLength(p1, p3)
	l3 := CalcuLength(p2, p3)
	p := (l1 + l2 + l3) / 2
	return math.Sqrt((p - l1) * (p - l2) * (p - l3) * p)
}

/*
使用面积方式判断:点是否在三角形内
*/
func TriangleContainByArea(p1, p2, p3 PointXy, p PointXy) bool {
	area1 := CalcuTriangleArea(p1, p2, p)
	area2 := CalcuTriangleArea(p1, p3, p)
	area3 := CalcuTriangleArea(p2, p3, p)
	areaAll := math.Round((area1 + area2 + area3) * 1000)
	triArea := math.Round(CalcuTriangleArea(p1, p2, p3) * 1000)
	return areaAll == triArea
}

/*
使用射线方式:点是否在三角形内， 支持在线段上，顶点
速度比面积快

1000W次

	utils_geometry_test.go:23: ByArea consume:1841(ms)
	utils_geometry_test.go:33: Ray consume:213(ms)
*/
func TriangleContainByRay(p1, p2, p3 PointXy, p PointXy) bool {
	xlist := make([]float64, 3)
	ylist := make([]float64, 3)
	xlist[0] = p1.X
	xlist[1] = p2.X
	xlist[2] = p3.X

	ylist[0] = p1.Y
	ylist[1] = p2.Y
	ylist[2] = p3.Y

	x, y := p.X, p.Y

	n := len(xlist)

	r := false
	i := 0
	j := n - 1
	for {
		if i >= n {
			break
		}

		b := (ylist[i] >= y) != (ylist[j] >= y)

		b = b && (x < (xlist[j]-xlist[i])*(y-ylist[i])/(ylist[j]-ylist[i])+xlist[i])
		if b {
			r = !r
		}
		j = i
		i++
	}

	return r
}
